//
// Created by gastonfeng on 2023/2/1.
//
#ifdef USE_SD
#include "SPI.h"
#include "logger_rte.h"
#include <STM32SD.h>

void listDir(const char *dirname, uint8_t levels) {
    logger.debug("Listing directory: %s\n", dirname);

    File root = SDClass::open(dirname);
    if (!root) {
        logger.debug("Failed to open directory");
        return;
    }
    if (!root.isDirectory()) {
        logger.debug("Not a directory");
        return;
    }

    File file = root.openNextFile();
    while (file) {
        if (file.isDirectory()) {
            logger.debug("  DIR : %s", file.name());
            if (levels) {
                listDir(file.name(), levels - 1);
            }
        } else {
            logger.debug("  FILE: %s SIZE: %d", file.name(), file.size());
        }
        file.close();
        file = root.openNextFile();
    }
    root.close();
}

void createDir(const char *path) {
    logger.debug("Creating Dir: %s\n", path);
    if (SDClass::mkdir(path)) {
        logger.debug("Dir created");
    } else {
        logger.debug("mkdir failed");
    }
}

void removeDir(const char *path) {
    logger.debug("Removing Dir: %s\n", path);
    if (SDClass::rmdir(path)) {
        logger.debug("Dir removed");
    } else {
        logger.debug("rmdir failed");
    }
}

void readFile(const char *path) {
    logger.debug("Reading file: %s\n", path);

    File file = SD.open(path);
    if (!file) {
        logger.debug("Failed to open file for reading");
        return;
    }

    logger.debug("Read from file: ");
    while (file.available()) {
        serial_putchar(file.read());
    }
    file.close();
}

void writeFile(const char *path, const char *message) {
    logger.debug("Writing file: %s\n", path);

    File file = SD.open(path, FILE_WRITE);
    if (!file) {
        logger.debug("Failed to open file for writing L77");
        return;
    }
    if (file.print(message)) {
        logger.debug("File written");
    } else {
        logger.debug("Write failed");
    }
    file.close();
}

void appendFile(const char *path, const char *message) {
    logger.debug("Appending to file: %s\n", path);

    File file = SD.open(path, FA_WRITE | FA_OPEN_APPEND);
    if (!file) {
        logger.debug("Failed to open file for appending");
        return;
    }
    if (file.print(message)) {
        logger.debug("Message appended");
    } else {
        logger.debug("Append failed");
    }
    file.close();
}

void renameFile(const char *path1, const char *path2) {
    logger.debug("Renaming file %s to %s\n", path1, path2);
    //    if (SD.rename(path1, path2)) {
    //        logger.debug("File renamed");
    //    } else {
    logger.debug("Rename failed");
    //    }
}

void deleteFile(const char *path) {
    logger.debug("Deleting file: %s\n", path);
    if (SDClass::remove(path)) {
        logger.debug("File deleted");
    } else {
        logger.debug("Delete failed");
    }
}

void testFileIO(const char *path) {
    File file = SD.open(path);
    static uint8_t buf[512];
    size_t len = 0;
    uint32_t start = millis();
    uint32_t end = start;
    if (file) {
        len = file.size();
        size_t flen = len;
        start = millis();
        while (len) {
            size_t toRead = len;
            if (toRead > 512) {
                toRead = 512;
            }
            file.read(buf, toRead);
            len -= toRead;
        }
        end = millis() - start;
        logger.debug("%u bytes read for %u ms\n", flen, end);
        file.close();
    } else {
        logger.debug("Failed to open file for reading");
    }

    file = SD.open(path, FILE_WRITE);
    if (!file) {
        logger.debug("Failed to open file for writing L150");
        return;
    }

    size_t i;
    start = millis();
    for (i = 0; i < 2048; i++) {
        file.write(buf, 512);
    }
    end = millis() - start;
    logger.debug("%u bytes written for %u ms\n", 2048 * 512, end);
    file.close();
}

void test_sd() {
    if (!SD.begin()) {
        logger.debug("Card Mount Failed");
        return;
    }
    uint8_t cardType = SD._card.type();

    if (cardType == SD_CARD_TYPE_UKN) {
        logger.debug("No SD card attached");
        return;
    }

    logger.debug("SD Card Type: ");
    if (cardType == SD_CARD_TYPE_SD1) {
        logger.debug("MMC");
    } else if (cardType == SD_CARD_TYPE_SD2) {
        logger.debug("SDSC");
    } else if (cardType == SD_CARD_TYPE_SDHC) {
        logger.debug("SDHC");
    } else {
        logger.debug("UNKNOWN");
    }
    float volumesize;
    if (4 == SD._fatFs.fatType()) {
        logger.info("Volume type is exFAT");
    } else {
        logger.info("Volume type is FAT%d", SD._fatFs.fatType());
    }

    float blocksPerCluster = SD._fatFs.blocksPerCluster();            // clusters are collections of blocks
    float clusterCount = SD._fatFs.clusterCount();                    // we'll have a lot of clusters
    volumesize = blocksPerCluster * clusterCount * 512 / 1024 / 1024; // SD card blocks are always 512 bytes
    uint32_t cardSize = volumesize;
    logger.debug("SD Card Size: %lluMB\n", cardSize);

    listDir("/", 0);
    createDir("/mydir");
    listDir("/", 0);
    removeDir("/mydir");
    listDir("/", 2);
    writeFile("/hello.txt", "Hello ");
    appendFile("/hello.txt", "World!\n");
    readFile("/hello.txt");
    deleteFile("/foo.txt");
    //    renameFile( "/hello.txt", "/foo.txt");
    writeFile("/foo.txt", "Hello ");
    readFile("/foo.txt");
    testFileIO("/test.txt");
    logger.debug("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
    logger.debug("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
}
#endif
